/*
 * Copyright (c) 2021, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.filter.convolve.border;

import boofcv.struct.border.ImageBorder;
import boofcv.struct.border.ImageBorder_IL_F32;
import boofcv.struct.border.ImageBorder_IL_S32;
import boofcv.struct.convolve.Kernel1D_F32;
import boofcv.struct.convolve.Kernel1D_S32;
import boofcv.struct.convolve.Kernel2D_F32;
import boofcv.struct.convolve.Kernel2D_S32;
import boofcv.struct.image.InterleavedF32;
import boofcv.struct.image.InterleavedI16;
import boofcv.struct.image.InterleavedS32;

import java.util.Arrays;

/**
 * <p>
 * Convolves just the image's border. How the border condition is handled is specified by the {@link ImageBorder}
 * passed in. For 1D kernels only the horizontal or vertical borders are processed.
 * </p>
 * 
 * <p>
 * WARNING: Do not modify. Automatically generated by GenerateConvolveJustBorder_General_IL.
 * </p>
 * 
 * @author Peter Abeles
 */
public class ConvolveJustBorder_General_IL {

	public static void horizontal(Kernel1D_F32 kernel, ImageBorder_IL_F32 src, InterleavedF32 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderRight = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		float pixel[] = new float[ numBands ];
		float total[] = new float[ numBands ];

		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offset; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					float valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-borderRight)*numBands;
			for ( int x = width-borderRight; x < width; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					float valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}
		}
	}

	public static void vertical(Kernel1D_F32 kernel, ImageBorder_IL_F32 src, InterleavedF32 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderBottom = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		float pixel[] = new float[ numBands ];
		float total[] = new float[ numBands ];

		for ( int x = 0; x < width; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offset; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x,y+k-offset,pixel);
					float valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}

			indexDst = dst.startIndex + (height-borderBottom) * dst.stride + x*numBands;
			for (int y = height-borderBottom; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++ ) {
					src.get(x,y+k-offset, pixel);
					float valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}
		}
	}

	public static void convolve(Kernel2D_F32 kernel, ImageBorder_IL_F32 src, InterleavedF32 dst ) {
		final int offsetL = kernel.getOffset();
		final int offsetR = kernel.getWidth()-offsetL-1;
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int numBands = dst.getNumBands();

		float pixel[] = new float[ numBands ];
		float total[] = new float[ numBands ];

		// convolve along the left and right borders
		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offsetL; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						float valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-offsetR)*numBands;
			for ( int x = width-offsetR; x < width; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						float valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}
		}

		// convolve along the top and bottom borders
		for ( int x = offsetL; x < width-offsetR; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offsetL; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						float valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}

			indexDst = dst.startIndex + (height-offsetR) * dst.stride + x*numBands;
			for (int y = height-offsetR; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						float valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}
		}
	}

	public static void horizontal(Kernel1D_S32 kernel, ImageBorder_IL_S32 src, InterleavedI16 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderRight = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offset; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = (short)total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-borderRight)*numBands;
			for ( int x = width-borderRight; x < width; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = (short)total[band];
				}
			}
		}
	}

	public static void vertical(Kernel1D_S32 kernel, ImageBorder_IL_S32 src, InterleavedI16 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderBottom = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		for ( int x = 0; x < width; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offset; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x,y+k-offset,pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = (short)total[band];
				}
			}

			indexDst = dst.startIndex + (height-borderBottom) * dst.stride + x*numBands;
			for (int y = height-borderBottom; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++ ) {
					src.get(x,y+k-offset, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = (short)total[band];
				}
			}
		}
	}

	public static void convolve(Kernel2D_S32 kernel, ImageBorder_IL_S32 src, InterleavedI16 dst ) {
		final int offsetL = kernel.getOffset();
		final int offsetR = kernel.getWidth()-offsetL-1;
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		// convolve along the left and right borders
		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offsetL; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = (short)total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-offsetR)*numBands;
			for ( int x = width-offsetR; x < width; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = (short)total[band];
				}
			}
		}

		// convolve along the top and bottom borders
		for ( int x = offsetL; x < width-offsetR; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offsetL; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = (short)total[band];
				}
			}

			indexDst = dst.startIndex + (height-offsetR) * dst.stride + x*numBands;
			for (int y = height-offsetR; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = (short)total[band];
				}
			}
		}
	}

	public static void horizontal(Kernel1D_S32 kernel, ImageBorder_IL_S32 src, InterleavedS32 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderRight = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offset; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-borderRight)*numBands;
			for ( int x = width-borderRight; x < width; x++ ) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x+k-offset,y, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band]*valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}
		}
	}

	public static void vertical(Kernel1D_S32 kernel, ImageBorder_IL_S32 src, InterleavedS32 dst ) {
		final int offset = kernel.getOffset();
		final int kernelWidth = kernel.getWidth();
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int borderBottom = kernelWidth-offset-1;
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		for ( int x = 0; x < width; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offset; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++) {
					src.get(x,y+k-offset,pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}

			indexDst = dst.startIndex + (height-borderBottom) * dst.stride + x*numBands;
			for (int y = height-borderBottom; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				for (int k = 0; k < kernelWidth; k++ ) {
					src.get(x,y+k-offset, pixel);
					int valueK = kernel.data[k];
					for (int band = 0; band < numBands; band++) {
						total[band] += pixel[band] * valueK;
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}
		}
	}

	public static void convolve(Kernel2D_S32 kernel, ImageBorder_IL_S32 src, InterleavedS32 dst ) {
		final int offsetL = kernel.getOffset();
		final int offsetR = kernel.getWidth()-offsetL-1;
		final int width = dst.getWidth();
		final int height = dst.getHeight();
		final int numBands = dst.getNumBands();

		int pixel[] = new int[ numBands ];
		int total[] = new int[ numBands ];

		// convolve along the left and right borders
		for (int y = 0; y < height; y++) {
			int indexDst = dst.startIndex + y * dst.stride;

			for ( int x = 0; x < offsetL; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}

			indexDst = dst.startIndex + y * dst.stride + (width-offsetR)*numBands;
			for ( int x = width-offsetR; x < width; x++ ) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst++] = total[band];
				}
			}
		}

		// convolve along the top and bottom borders
		for ( int x = offsetL; x < width-offsetR; x++ ) {
			int indexDst = dst.startIndex + x*numBands;

			for (int y = 0; y < offsetL; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}

			indexDst = dst.startIndex + (height-offsetR) * dst.stride + x*numBands;
			for (int y = height-offsetR; y < height; y++, indexDst += dst.stride) {
				Arrays.fill(total,0);
				int indexKer = 0;
				for( int i = -offsetL; i <= offsetR; i++ ) {
					for (int j = -offsetL; j <= offsetR; j++) {
						src.get(x+j,y+i, pixel);
						int valueK = kernel.data[indexKer++];
						for (int band = 0; band < numBands; band++) {
							total[band] += pixel[band] * valueK;
						}
					}
				}
				for (int band = 0; band < numBands; band++) {
					dst.data[indexDst + band] = total[band];
				}
			}
		}
	}

}
